package com.ftwj.demo.global.aop;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ftwj.demo.config.redis.RedisUtil;
import com.ftwj.demo.utils.result.ResultBean;
import com.google.common.util.concurrent.RateLimiter;
/**
*
* <p>Title: CurrentLimitAcpect</p>  
* <p>Description: 描述： 通过切面的限流阻止短时间内的同一用户的多次刷新或提交操作</p>
* <p>Copyright: Copyright (c) 2020 </p>  
* <p>Company: 兰州飞天网景信息产业有限公司</p>    
* @author WangSLi,HeYang
* @date 2020年5月20日 上午10:45:18
* @version V1.0
 */
//TODO 通过AOP切面限流阻止短时间多次刷新
// @Scope 默认单例此注解无意义（何洋）
@Component
@Aspect
@Order(0)
public class RateLimitAspect {
	private Logger log = LoggerFactory.getLogger(this.getClass());

	// 用来存放不同接口的RateLimiter(key为接口名称，value为RateLimiter)
	private ConcurrentHashMap<String, RateLimiter> map = new ConcurrentHashMap<>();

	private static ObjectMapper objectMapper = new ObjectMapper();

	private RateLimiter rateLimiter;

	@Autowired
	private HttpServletResponse response;
	@Autowired
	private RedisUtil redis;

	// @Pointcut("execution(public *
	// com.ftwj.demo.**.controller.*Controller..*(..)))")
	@Pointcut("@annotation(RateLimit)")
	public void serviceLimit() {

	}

	@Around("serviceLimit()")
    public Object around(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
		
        // 接收到请求，记录请求内容
//        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        HttpServletRequest request = attributes.getRequest();
        
        Object obj = null;
        //获取拦截的方法名
        Signature sig = joinPoint.getSignature();
        //获取拦截的方法名
        MethodSignature msig = (MethodSignature) sig;
        //返回被织入增加处理目标对象
        Object target = joinPoint.getTarget();
        //为了获取注解信息
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        //获取注解信息
        RateLimit annotation = currentMethod.getAnnotation(RateLimit.class);
        int num = annotation.limit(); //获取注解每秒加入桶中的token
        String functionName = msig.getName(); // 注解所在方法名区分不同的限流策略
        //获取rateLimiter
        if(map.containsKey(functionName)){
        	rateLimiter = map.get(functionName);
        }else {
            map.put(functionName, RateLimiter.create(num));
            rateLimiter = map.get(functionName);
        }
         
       log.info("【AOP动态接口限流】，当前代理对象：{}，方法：{}" ,target.toString(), functionName);
       String key=target.toString()+functionName;
       Object res = redis.get(key);
       
       try {
    	   if (rateLimiter.tryAcquire() && res==null) {//如果该用户存在则不可调用方法
    		   //执行方法
    		   obj = joinPoint.proceed();
    		   redis.set(key, key, 1);
    	   } else {
    		   //拒绝了请求（服务降级）
    		   String result = objectMapper.writeValueAsString(ResultBean.error(-300, "服务已限流，每秒请求次数过多！"));
    		   outErrorResult(result);
    	   }
       } catch (Throwable throwable) {
    	   log.error("【限流异常】：",  throwable);
       }
       return obj;
    }

	// 将结果返回
	public void outErrorResult(String result) {
		response.setContentType("application/json;charset=UTF-8");
		try (ServletOutputStream outputStream = response.getOutputStream()) {
			outputStream.write(result.getBytes("utf-8"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}